package com.infinova.datax.apireader;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.datax.common.element.Column;
import com.alibaba.datax.common.element.LongColumn;
import com.alibaba.datax.common.element.StringColumn;
import com.alibaba.datax.common.util.Configuration;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.infinova.datax.common.http.HttpMethod;
import com.infinova.datax.common.http.HttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.List;
import java.util.Optional;

/**
 * @author bigbird-0101
 * @date 2023-07-24 16:16
 */
public class Config {
    private HttpMethod method;
    private String host;
    private String protocol;
    private Integer port;
    private String requestPath;

    private Boolean enablePage;

    private PageType pageType;
    private List<Param> params;
    /**
     * 结果jsonPath
     */
    private String resultDataPath;

    /**
     * 数据模型
     */
    private List<DataModel> dataModels;

    private Segment segment;

    public Segment getSegment() {
        return segment;
    }

    public void setSegment(Segment segment) {
        this.segment = segment;
    }

    public static class Param{
        private WhereMapping whereMapping;
        private String key;

        /**
         * 是否是页码字段/序列号字段
         */
        private Boolean page;

        /**
         * 是否是分页大小字段
         */
        private Boolean pageSize;

        private Object value;
        public Param() {
        }

        public Param(WhereMapping whereMapping, String key, Object value) {
            this.whereMapping = whereMapping;
            this.key = key;
            this.value = value;
        }

        public Param(WhereMapping whereMapping, String key, Boolean page, Boolean pageSize, Object value) {
            this.whereMapping = whereMapping;
            this.key = key;
            this.page = page;
            this.pageSize = pageSize;
            this.value = value;
        }

        public WhereMapping getWhereMapping() {
            return whereMapping;
        }

        public void setWhereMapping(WhereMapping whereMapping) {
            this.whereMapping = whereMapping;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public Boolean getPage() {
            return page;
        }

        public void setPage(Boolean page) {
            this.page = page;
        }

        public Boolean getPageSize() {
            return pageSize;
        }

        public void setPageSize(Boolean pageSize) {
            this.pageSize = pageSize;
        }
    }

    public static class DataModel {
        /**
         * 字段名
         */
        private String key;
        /**
         * 字段
         */
        private ValueType valueType;
        /**
         * 描述
         */
        private String desc;


        public DataModel() {
        }

        public DataModel(String key, ValueType valueType, String desc) {
            this.key = key;
            this.valueType = valueType;
            this.desc = desc;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public ValueType getValueType() {
            return valueType;
        }

        public void setValueType(ValueType valueType) {
            this.valueType = valueType;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }
    }


    public enum WhereMapping{
        QUERY,
        HEADER,
        BODY,

        ;
    }

    public enum ValueType{
        /**
         * string
         */
        STRING{
            @Override
            Column convertTarget(Object param) {
                return new StringColumn(Convert.convert(String.class,param));
            }
        },
        /**
         * int
         */
        INT{
            @Override
            Column convertTarget(Object param) {
                return new LongColumn(Convert.convert(Integer.class,param));
            }
        },
        /**
         * int
         */
        OBJECT{
            @Override
            Column convertTarget(Object param) {
                return new StringColumn(JSON.toJSONString(param));
            }
        },
        /**
         * long
         */
        LONG{
            @Override
            Column convertTarget(Object param) {
                return new LongColumn(Convert.convert(Long.class,param));
            }
        },
        /**
         * array
         */
        ARRAY{
            @Override
            Column convertTarget(Object param) {
                return new StringColumn(JSON.toJSONString(param));
            }
        }
        ;

        abstract Column convertTarget(Object param);
    }

    public HttpMethod getMethod() {
        return method;
    }

    public void setMethod(HttpMethod method) {
        this.method = method;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public String getRequestPath() {
        return requestPath;
    }

    public void setRequestPath(String requestPath) {
        this.requestPath = requestPath;
    }

    public List<Param> getParams() {
        return params;
    }

    public void setParams(List<Param> params) {
        this.params = params;
    }

    public String getResultDataPath() {
        return resultDataPath;
    }

    public void setResultDataPath(String resultDataPath) {
        this.resultDataPath = resultDataPath;
    }

    public List<DataModel> getDataModels() {
        return dataModels;
    }

    public void setDataModels(List<DataModel> dataModels) {
        this.dataModels = dataModels;
    }


    public Boolean getEnablePage() {
        return enablePage;
    }

    public void setEnablePage(Boolean enablePage) {
        this.enablePage = enablePage;
    }

    public PageType getPageType() {
        return pageType;
    }

    public void setPageType(PageType pageType) {
        this.pageType = pageType;
    }

    public static Config convert(Configuration configuration){
        String json = configuration.toJSON();
        return JSONObject.parseObject(json, Config.class);
    }

    public HttpRequest convert(){
        HttpRequest httpRequest=new HttpRequest();
        httpRequest.setMethod(this.getMethod());
        String host = this.getHost();
        String protocol = ObjectUtil.defaultIfNull(this.getProtocol(),"http://");
        Integer port = this.getPort();
        String requestPath =this.getRequestPath();
        httpRequest.setUrl(protocol+host+":"+port+requestPath);
        List<Param> paramList = this.getParams();
        paramList.forEach(s-> setHttpRequestParam(httpRequest,s));
        return httpRequest;
    }

    public boolean canPaging(){
        return BooleanUtil.isTrue(enablePage)&&null!=pageType;
    }

    public Iterator<HttpRequest> convertIterator(){
        return new HttpRequestIterator(this);
    }

    protected static class HttpRequestIterator implements Iterator<HttpRequest> {
        private final static Logger LOGGER = LoggerFactory.getLogger("com.infinova.datax.apireader.Config.HttpRequestIterator");
        private final Config config;
        private final boolean canPaging;
        private boolean canPageFlag;
        private final long pageSize;
        private long page;

        /**
         * 迭代的次数
         */
        private int loopThreshold;

        public HttpRequestIterator(Config config) {
            this.config=config;
            canPaging = config.canPaging();
            page= Optional.ofNullable(config.getSegment()).map(Segment::getIndex).orElse(0L);
            pageSize=config.getPageSizeParam().map(s->Long.valueOf(String.valueOf(s.getValue()))).orElse(100L);
        }

        @Override
        public boolean hasNext() {
            if(!canPageFlag&&!canPaging){
                canPageFlag=true;
                return true;
            }
            if(canPaging){
                LOGGER.info("当前循环次数 [{}]",loopThreshold);
                if((loopThreshold++)>= config.getSegment().getRequestCount()){
                    LOGGER.warn("超过最大循环调用次数最大阀值");
                    return false;
                }
                return true;
            }
            return false;
        }

        @Override
        public HttpRequest next() {
            if(!canPaging){
                return config.convert();
            }else {
                HttpRequest httpRequest = config.convert();
                Optional<Param> pageParam = config.getPageParam();
                Optional<Param> pageSizeParam = config.getPageSizeParam();
                pageParam.ifPresent(s-> {
                    s.setValue(page);
                    setHttpRequestParam(httpRequest, s);
                });
                pageSizeParam.ifPresent(s-> {
                    s.setValue(pageSize);
                    setHttpRequestParam(httpRequest, s);
                });
                if(PageType.PAGE.equals(config.getPageType())){
                    page=page+1;
                }else{
                    page=page+pageSize;
                }
                return httpRequest;
            }
        }
    }

    private static void setHttpRequestParam(HttpRequest httpRequest, Param s) {
        WhereMapping whereMapping = s.getWhereMapping();
        if(WhereMapping.BODY.equals(whereMapping)){
            JSONObject body = httpRequest.getBody();
            if(MapUtil.isEmpty(body)){
                body=new JSONObject();
                httpRequest.setBody(body);
            }
            Object value = s.getValue();
            if(value instanceof String){
                try{
                    Object parse = JSON.parse((String) value);
                    body.put(s.getKey(), parse);
                }catch (JSONException jsonException){
                    body.put(s.getKey(), s.getValue());
                }
            }else {
                body.put(s.getKey(), s.getValue());
            }
        }else if(WhereMapping.HEADER.equals(whereMapping)){
            JSONObject header = httpRequest.getHeader();
            if(MapUtil.isEmpty(header)){
                header=new JSONObject();
                httpRequest.setHeader(header);
            }
            header.put(s.getKey(), s.getValue());
        }else if(WhereMapping.QUERY.equals(whereMapping)){
            JSONObject query = httpRequest.getQuery();
            if(MapUtil.isEmpty(query)){
                query=new JSONObject();
                httpRequest.setQuery(query);
            }
            query.put(s.getKey(), s.getValue());
        }
    }

    protected Optional<Param> getPageSizeParam() {
        return Optional.ofNullable(this.getParams())
                .flatMap(s -> s.stream().filter(b -> BooleanUtil.isTrue(b.getPageSize())).findFirst());
    }

    protected Optional<Param> getPageParam() {
        return Optional.ofNullable(this.getParams())
                .flatMap(s -> s.stream().filter(b -> BooleanUtil.isTrue(b.getPage())).findFirst());
    }

    public enum PageType {
        /**
         * 页码分页
         */
        PAGE,
        /**
         * 序列号分页
         */
        SERIAL_NUMBER
    }
}
